/**
 * Copyright 2007-2008 Michele Mostarda ( michele.mostarda@gmail.com ).
 * All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.asemantics.anthill;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
import org.asemantics.anthill.chatconsole.ChatConsoleOutputStream;
import org.asemantics.anthill.chatconsole.Command;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

/**
 * This class models the <i>inspectAdded</i> node, useful to obtain the list
 * of available inspectAdded of a <i>context</i> listener.
 * //TODO: add properties retieval.
 */
public class Inspect extends RemoteOperation implements Command {

    /* BEGIN: Targets node attributes. */

    /**
     * The name of the property containing the list of tagets separated by <i>targetsSeparator</i>.
     */
    private String targetsProperty;

    /**
     * The separator to be used to generate the list of inspect in <i>targetsProperty</i>.
     */
    private String targetsSeparator;

    /**
     * The name of the property containing the list of target descriptions.
     */
    private String targetDescriptionsProperty;

    /**
     * The separator to be used to generate the list of inspect in <i>targetDescriptionsProperty</i>.
     */
    private String targetDescriptionsSeparator;

    /**
     * The property used to contain the list of the remote properties retrieved during inspection.
     */
    private String remotePropertiesProperty;

    /**
     * The separator used to define the list of the remote properties.
     */
    private String remotePropertiesSeparator;

    /* END: Targets node attributes. */

    public Inspect() {}

    /**
     * Returns the <i>targetsProperty</i>.
     *
     * @return
     */
    public String getTargetsProperty() {
        return targetsProperty;
    }

    /**
     * Sets the targetsProperty.
     *
     * @param targetsProperty
     */
    public void setTargetsProperty(String targetsProperty) {
        CommanderTask.validateAttribute(targetsProperty, "targetsProperty");
        this.targetsProperty = targetsProperty;
    }

    /**
     * Returns the target description property.
     *
     * @return
     */
    public String getTargetDescriptionsProperty() {
        return targetDescriptionsProperty;
    }

    /**
     * Sets the target description property.
     *
     * @param targetDescriptionProperty
     */
    public void setTargetDescriptionsProperty(String targetDescriptionProperty) {
        CommanderTask.validateAttribute(targetDescriptionProperty, "targetDescriptionProperty");
        this.targetDescriptionsProperty = targetDescriptionProperty;
    }

    /**
     * Returns the targets separator string.
     * it is composed by a single character.
     *
     * @return
     */
    public String getTargetsSeparator() {
        return targetsSeparator;
    }

    /**
     * Sets the targets separator string.
     * It must be composed by a single character.
     *
     * @param targetsSeparator
     */
    public void setTargetsSeparator(String targetsSeparator) {
        CommanderTask.validateAttribute(targetsSeparator, "targetsSeparator");
        //Verifies that the property is a single char.
        if (targetsSeparator.length() > 1) {
            throw new BuildException("invalid targetSeparator '" + targetsSeparator + "' : must be a single char.");
        }
        this.targetsSeparator = targetsSeparator;
    }

    /**
     * Returns the target description separator.
     * It is composed by a single character.
     *
     * @return
     */
    public String getTargetDescriptionsSeparator() {
        return targetDescriptionsSeparator;
    }

    /**
     * Sets the target description separator.
     * It must be a string with a single character.
     *
     * @param targetDescriptionsSeparator
     */
    public void setTargetDescriptionsSeparator(String targetDescriptionsSeparator) {
        CommanderTask.validateAttribute(targetDescriptionsSeparator, "targetDescriptionsSeparator");
        //Verifies that the property is a single char.
        if (targetsSeparator.length() > 1) {
            throw new BuildException("invalid targetDescriptionSeparator '" + targetDescriptionsSeparator + "' : must be a single char.");
        }
        this.targetDescriptionsSeparator = targetDescriptionsSeparator;
    }

    /**
     * Returns the name of th property storinf the remote properties list.
     *
     * @return
     */
    public String getRemotePropertiesProperty() {
        return remotePropertiesProperty;
    }

    /**
     * Sets the name of the property containing the list of the retrieved remote properties.
     *
     * @param remotePropertiesProperty
     */
    public void setRemotePropertiesProperty(String remotePropertiesProperty) {
        CommanderTask.validateAttribute(remotePropertiesProperty, "remotePropertiesProperty");
        this.remotePropertiesProperty = remotePropertiesProperty;
    }

    /**
     * Returns the separator for the remote porperties list.
     * 
     * @return
     */
    public String getRemotePropertiesSeparator() {
        return remotePropertiesSeparator;
    }

    /**
     * Sets the separator for the remote properties list.
     * Such separator must be a not empty single char.
     *
     * @param remotePropertiesSeparator
     */
    public void setRemotePropertiesSeparator(String remotePropertiesSeparator) {
        CommanderTask.validateAttribute(remotePropertiesSeparator, "remotePropertiesSeparator");
        //Verifies that the property is a single char.
        if (targetsSeparator.length() > 1) {
            throw new BuildException("invalid remotePropertiesSeparator '" + remotePropertiesSeparator + "' : must be a single char.");
        }

        this.remotePropertiesSeparator = remotePropertiesSeparator;
    }

    public CommandMessage prepareMessageInternal() {
        CommandMessage command = new InspectCommandMessage(InspectCommandMessage.NO_PARAMETERS);
        return command;
    }

    public CommandResponseMessage processCommand(CommandListenerTask clt, CommandMessage commandMessage) {
        Map<String,String> TargetsMap  = retrieveAvailableTargets(clt);
        Map<String,String> environment = retrieveEnvironment(clt);         
        InspectCommandResponseMessage resultMessage = new InspectCommandResponseMessage(TargetsMap, environment, true);
        return resultMessage;
    }

    public void processResult(final CommanderTask commanderTask, final CommandResponseMessage response) {
        // Retrieve data.
        Map<String,String> targetsMap     = InspectCommandResponseMessage.getTargetParameters(response);
        Map<String,String> environmentMap = InspectCommandResponseMessage.getEnvironmentProperties(response);

        // Show data.
        commanderTask.log("Inspection result {", Project.MSG_INFO);
        for (Map.Entry<String, String> s : targetsMap.entrySet()) {
            commanderTask.log("target: " + s.getKey() + " : " + s.getValue(), Project.MSG_INFO);
        }
        for (Map.Entry<String, String> s : environmentMap.entrySet()) {
            commanderTask.log("remote property: " + s.getKey() + " : " + s.getValue(), Project.MSG_INFO);
        }
        commanderTask.log("}", Project.MSG_INFO);

        // Load data.
        commanderTask.loadTasklistPropertiesInProject(
                getTargetsProperty(),
                getTargetsSeparator(),
                getTargetDescriptionsProperty(),
                getTargetDescriptionsSeparator(),
                targetsMap
        );
        commanderTask.loadRemoteEnvPropertiesInProject(
                getRemotePropertiesProperty(),
                getRemotePropertiesSeparator(),
                environmentMap
        );
    }

    public boolean execute(CommandListenerTask clt, ChatConsoleOutputStream chatConsole, String commandName, String[] arguments) {
        chatConsole.println("Actually I don't return any information...");
        return true;
    }

    /**
     * Retrieves the list of the available <i>target</i>s exposed by the remote listener.
     *
     * @param listenerTask
     * @return
     */
    private static Map<String,String> retrieveAvailableTargets(CommandListenerTask listenerTask) {
        Map<String, Target> targets = listenerTask.getProject().getTargets();
        Set<String> targetNames = targets.keySet();
        Map<String,String> map = new HashMap<String,String>();
        for(String target : targetNames) {
            map.put(target, targets.get(target).getDescription());
        }
        return map;
    }

    /**
     * Retrieves the map of the available properties.
     *
     * @param listenerTask
     * @return
     */
    private static Map<String,String> retrieveEnvironment(CommandListenerTask listenerTask) {
        Hashtable properties = listenerTask.getProject().getProperties();
        Map<String,String> result = new HashMap<String,String>(properties);
        return result;
    }
}
